﻿using System;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;
using Anderson.Utils;

namespace Anderson.PluginFramework
{
    public class Plugin : IPlugin
    {
        #region Constructor
        public Plugin(IFramework framework, ILog log, PluginEntity entity)
        {
            m_framework = framework;
            m_log = log;
            m_entity = entity;
        }
        #endregion

        #region Properties
        private readonly object PluginLock = new object();

        PluginEntity m_entity;
        IFramework m_framework;
        ILog m_log;
        PluginEventType m_state = PluginEventType.UnResolve;
        bool m_resolved;
        bool m_started;

        Assembly pluginAssembly = null;
        bool assemblyLoaded = false;
        bool activatorFound = false;
        Type PluginActivatorType;
        IPluginActivator m_activatorInstance;
        bool m_isGameObjectActivator = false;

        IDictionary<Type, object> dictRegisteredServices = null;

        public IPluginActivator ActivatorInstance
        {
            get
            {
                return m_activatorInstance;
            }
        }

        public bool IsGameObjectActivator
        {
            get
            {
                return m_isGameObjectActivator;
            }
        }
        #endregion

        #region IPlugin        
        public PluginEntity Entity
        {
            get
            {
                return m_entity;
            }

            set
            {
                m_entity = value;
            }
        }

        public IFramework Framework
        {
            get
            {
                return m_framework;
            }

            set
            {
                m_framework = value;
            }
        }

        public ILog Log
        {
            get
            {
                return m_log;
            }
            set
            {
                m_log = value;
            }
        }

        public PluginEventType State
        {
            get
            {
                return m_state;
            }
        }

        public bool Resolved
        {
            get
            {
                return m_resolved;
            }

            set
            {
                m_resolved = value;
            }
        }

        public bool Started
        {
            get
            {
                return m_started;
            }

            set
            {
                m_started = value;
            }
        }

        /// <summary>
        /// 外部必须把PluginEntity的TextAsset准备好
        /// Plugin只负责从已经加载的TextAsset读取Assembly信息
        /// </summary>
        public void Load()
        {
            lock (PluginLock) 
            {
                if (!m_resolved)
                {
                    m_log.Debug("加载插件-开始 : " + m_entity.pluginName);

                    PluginResolve();

                    m_log.Debug("加载插件-结束 : " + m_entity.pluginName);
                }
            }
        }

        public void Run()
        {
            lock (PluginLock) 
            {
                if (m_resolved && !m_started && activatorFound)
                {
                    m_log.Debug("启动插件-开始 : " + m_entity.pluginName);

                    PluginRun();

                    m_log.Debug("启动插件-结束 : " + m_entity.pluginName);
                }
            }
        }

        public void Stop()
        {
            lock (PluginLock) 
            {
                if (m_resolved && m_started)
                {
                    m_log.Debug("停止插件-开始 : " + m_entity.pluginName);

                    PluginStop();

                    m_log.Debug("停止插件-结束 : " + m_entity.pluginName);
                }
            }
        }

        public void Uninstall()
        {            
            lock (PluginLock)
            {
                if (m_resolved && m_started)
                {
                    m_log.Debug("卸载插件-开始 : " + m_entity.pluginName);

                    PluginUninstall();

                    m_log.Debug("卸载插件-开始 : " + m_entity.pluginName);
                }
            }
        }
        #endregion

        #region IPlugin.Inner
        void PluginResolve()
        {
            //二进制内容有效？
            if (null == m_entity || null == m_entity.assemblyAsset)
            {
                m_log.Error("加载插件错误，插件内容为空");
                return;
            }

            //从二进制内容中读取程序集成功？
            try
            {
                pluginAssembly = Assembly.Load(m_entity.assemblyAsset.bytes);
                Log.Debug("加载程序集：" + pluginAssembly.FullName);//DLL的名字
                assemblyLoaded = true;
            }
            catch (Exception e)
            {
                m_log.Error("启动程序集错误，解析程序集异常");
                m_log.Error(e.Message);

                assemblyLoaded = false;
                pluginAssembly = null;

                return;
            }

            //判断启动器存在？            
            Type[] types = pluginAssembly.GetTypes();
            object serviceInstance = null;
            foreach (Type t in types)
            {
                Log.Debug("Class : " + t.FullName);
                //if (!activatorFound && t.IsSubclassOf(typeof(IPluginActivator)))
                if (!activatorFound && typeof(IPluginActivator).IsAssignableFrom(t) && !t.IsInterface)
                {
                    PluginActivatorType = t;
                    activatorFound = true;

                    if (typeof(MonoBehaviour).IsAssignableFrom(t))
                    {
                        m_isGameObjectActivator = true;
                    }
                }

                //注册服务
                if (typeof(IService).IsAssignableFrom(t) && !t.IsInterface)
                {
                    IServiceBus bus = m_framework;

                    if (typeof(MonoBehaviour).IsAssignableFrom(t))
                    {
                        //MonoBehaviour类型的Service不自动创建
                        serviceInstance = null;
                    }
                    else
                    {
                        serviceInstance = Activator.CreateInstance(t);
                    }
                    bus.RegisterService(this, t, serviceInstance);

                    if (null == dictRegisteredServices)
                    {
                        dictRegisteredServices = new Dictionary<Type, object>();
                    }

                    dictRegisteredServices.Add(t, serviceInstance);
                }
            }

            //激活器
            if (activatorFound)
            {
                try
                {
                    if (m_isGameObjectActivator)
                    {
                        GameObject go = new GameObject();
                        go.name = PluginActivatorType.Name;
                        go.transform.parent = m_framework.PluginRootTransform;

                        m_activatorInstance = (IPluginActivator)go.AddComponent(PluginActivatorType);
                    }
                    else
                    {
                        m_activatorInstance = (IPluginActivator)Activator.CreateInstance(PluginActivatorType);
                    }

                    //初始化
                    m_activatorInstance.Init(m_framework, this);

                    //事件监听器
                    m_log.Debug("事件监听器 : " + Entity.pluginName);
                    IContextListener contextListener = m_framework;
                    if (typeof(IPluginListener).IsAssignableFrom(PluginActivatorType))
                    {
                        IPluginListener listener = (IPluginListener)m_activatorInstance;
                        contextListener.AddPluginListener(listener.PluginChanged);
                    }
                    if (typeof(IServiceListener).IsAssignableFrom(PluginActivatorType))
                    {
                        IServiceListener listener = (IServiceListener)m_activatorInstance;
                        contextListener.AddServiceListener(listener.ServiceChanged);
                    }
                    if (typeof(IFrameworkListener).IsAssignableFrom(PluginActivatorType))
                    {
                        IFrameworkListener listener = (IFrameworkListener)m_activatorInstance;
                        contextListener.AddFrameworkListener(listener.FrameworkChanged);
                    }
                }
                catch (Exception e)
                {
                    m_log.Error("激活器解析错误");
                    m_log.Error(e.Message);

                    m_activatorInstance = null;

                    return;
                }
            }            

            //解析成功
            m_resolved = true;
            m_state = PluginEventType.Resolved;
            m_framework.FirePluginEvent(new PluginEventArg(m_state, this));
        }

        void PluginRun()
        {
            //程序集解析完成
            m_state = PluginEventType.Starting;
            m_framework.FirePluginEvent(new PluginEventArg(m_state, this));

            //启动激活器
            m_activatorInstance.Run();

            m_started = true;
            m_state = PluginEventType.Started;
            m_framework.FirePluginEvent(new PluginEventArg(m_state, this));
        }

        void PluginStop()
        {
            if (m_state != PluginEventType.Started)
            {
                return;
            }

            if (activatorFound && null != m_activatorInstance)
            {
                m_state = PluginEventType.Stopping;
                m_framework.FirePluginEvent(new PluginEventArg(m_state, this));

                m_activatorInstance.Stop();

                m_started = false;
                m_state = PluginEventType.Stopped;
                m_framework.FirePluginEvent(new PluginEventArg(m_state, this));
            }
        }

        void PluginUninstall()
        {
            if (m_state == PluginEventType.UnResolve)
            {
                return;
            }

            if (m_state == PluginEventType.Started)
            {
                PluginStop();
            }

            //注销事件监听器 
            if (activatorFound && null != m_activatorInstance)
            {
                IContextListener contextListener = m_framework;
                if (typeof(IPluginListener).IsAssignableFrom(PluginActivatorType))
                {
                    IPluginListener listener = (IPluginListener)m_activatorInstance;
                    contextListener.RemovePluginListener(listener.PluginChanged);
                }
                if (typeof(IServiceListener).IsAssignableFrom(PluginActivatorType))
                {
                    IServiceListener listener = (IServiceListener)m_activatorInstance;
                    contextListener.RemoveServiceListener(listener.ServiceChanged);
                }
                if (typeof(IFrameworkListener).IsAssignableFrom(PluginActivatorType))
                {
                    IFrameworkListener listener = (IFrameworkListener)m_activatorInstance;
                    contextListener.RemoveFrameworkListener(listener.FrameworkChanged);
                }
            }

            //注销已经注册的服务
            if (null != dictRegisteredServices)
            {
                IServiceBus bus = m_framework;
                foreach (KeyValuePair<Type, object> item in dictRegisteredServices)
                {
                    bus.UnRegisterService(this, item.Key, item.Value);
                }
            }

            //注销已经启动的激活器
            //如果是GameObject型激活器，则删除对象
            if (activatorFound)
            {
                if (m_isGameObjectActivator)
                {
                    MonoBehaviour component = (MonoBehaviour)m_activatorInstance;
                    GameObject go = component.gameObject;
                    GameObject.Destroy(go);
                }

                m_activatorInstance = null;
            }

            //变量值初始化
            pluginAssembly = null;
            assemblyLoaded = false;
            activatorFound = false;
            m_isGameObjectActivator = false;
            dictRegisteredServices = null;

            m_resolved = false;
            m_state = PluginEventType.UnResolve;
            m_framework.FirePluginEvent(new PluginEventArg(m_state, this));
        }
        #endregion
    }
}